package be.rivendale.mathematics;

import static be.rivendale.mathematics.MathematicalAssert.assertPointEquals;
import static be.rivendale.mathematics.MathematicalAssert.assertRealEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test;

public class RayTest {
    @Test
    public void constructorByOriginPointAndInteresectionPointSetsOrigin() throws Exception {
        Ray ray = new Ray(new Triple(1, 2, 3), new Triple(4, 5, 6));
        assertPointEquals(new Triple(1, 2, 3), ray.getOrigin());
    }

    @Test
    public void constructorByOriginPointAndInteresectionPointSetsIntersectionPoint() throws Exception {
        Ray ray = new Ray(new Triple(1, 2, 3), new Triple(4, 5, 6));
        assertPointEquals(new Triple(4, 5, 6), ray.getPassThroughPoint());
    }

    @Test(expected = IllegalArgumentException.class)
    public void constructorByOriginPointAndInteresectionPointThrowsIllegalArgumentExceptionWhenOriginIsNull() throws Exception {
        new Ray(null, new Triple(1, 2, 3));
    }

    @Test(expected = IllegalArgumentException.class)
    public void constructorByOriginPointAndInteresectionPointThrowsIllegalArgumentExceptionWhenIntersectionIsNull() throws Exception {
        new Ray(new Triple(1, 2, 3), null);
    }

    @Test(expected = IllegalArgumentException.class)
    public void constructorByOriginPointAndInteresectionPointThrowsIllegalArgumentExceptionWhenIntersectionAndOriginAreEqual() throws Exception {
        new Ray(new Triple(1, 2, 3), new Triple(1, 2, 3));
    }

    @Test
    public void getDirectionReturnsVectorBetweenOriginAndSecondPoint() {
        Triple origin = new Triple(4, 8, 12);
        Triple point = new Triple(-6, 3, 5);
        Ray ray = new Ray(origin, point);
        assertEquals(Triple.vectorBetweenPoints(origin, point), ray.direction());
    }

    @Test
    public void pointOnRayReturnsPointEqualsToSecondaryPointWhenMultiplicationFactorIsOne() throws Exception {
        Ray ray = new Ray(new Triple(-3, -5, -3), new Triple(3, 2, 1));
        assertPointEquals(ray.getPassThroughPoint(), ray.pointOnRay(1));
    }

    @Test
    public void pointOnRayReturnsPointIfRepresentedAsVectorIsParallelToTheRayDirectionVector() throws Exception {
        Ray ray = new Ray(new Triple(3, 5, -3), new Triple(3, -2, -1));
        Point point = ray.pointOnRay(1.5);
        Vector vectorFromOriginToNewPoint = Triple.vectorBetweenPoints(ray.getOrigin(), point);
        assertTrue(ray.direction().isParallelTo(vectorFromOriginToNewPoint));
    }

    @Test
    public void pointOnRayReturnsCorrectPointOnRay() throws Exception {
        Ray ray = new Ray(new Triple(5, 6, 7), new Triple(8, 7, 6));
        Point pointOnRay = ray.pointOnRay(3);
        assertPointEquals(new Triple(14, 9, 4), pointOnRay);
    }

    @Test(expected = IllegalArgumentException.class)
    public void pointOnRayThrowsIllegalArgumentExceptionWhenPassingANegativeValue() throws Exception {
        Ray ray = new Ray(new Triple(5, 6, 7), new Triple(8, 7, 6));
        Point pointOnRay = ray.pointOnRay(-3);
    }

    @Test
    public void pointOnRayReturnsPointIfRepresentedAsVectorIsExactlyAsLongAsTheRayDirectionTimesTheMultiplicationFactor() throws Exception {
        Ray ray = new Ray(new Triple(-3, 7, 9), new Triple(5, 4, 1));
        Point point = ray.pointOnRay(1.75);
        Vector vectorFromOriginToNewPoint = Triple.vectorBetweenPoints(ray.getOrigin(), point);
        assertRealEquals(ray.direction().length() * 1.75, vectorFromOriginToNewPoint.length());

    }

    @Test
    public void pointOnRayReturnsPointEqualToOriginWhenMultiplicationFactorIsZero() throws Exception {
        Ray ray = new Ray(new Triple(0, 0, -5), new Triple(6, 2, 8));
        assertPointEquals(ray.getOrigin(), ray.pointOnRay(0));
    }
}
